package com.neuronbit.businessflow.intellij.editor;

import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.registry.RegistryValueListener;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxSwingConstants;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEventSource;
import com.mxgraph.view.mxGraph;
import com.neuronbit.businessflow.intellij.editor.swing.GuiEditor;
import com.neuronbit.businessflow.intellij.editor.swing.editor.RadComponent;
import com.neuronbit.businessflow.intellij.model.JavaMethodTask;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.*;

/**
 * The type Bpm swing file editor.
 */
public class BpmSwingFileEditor implements FileEditor, @NotNull RegistryValueListener {
    private final Project project;
    private final VirtualFile file;
    private final GuiEditor editor;
    private final PsiFile psiFile;
    private FileEditorState state;
    private boolean isModified;
    private List listeners = new ArrayList();
    private Map userData = new HashMap();

    /**
     * Instantiates a new Bpm swing file editor.
     *
     * @param project the project
     * @param file    the file
     */
    public BpmSwingFileEditor(@NotNull Project project, VirtualFile file) {
        this.project = project;
        this.file = file;
//        try
//        {
//            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//        }
//        catch (Exception e1)
//        {
//            e1.printStackTrace();
//        }

        mxSwingConstants.SHADOW_COLOR = Color.LIGHT_GRAY;
        mxConstants.W3C_SHADOWCOLOR = "#D3D3D3";
        psiFile = PsiManager.getInstance(project).findFile(file);
        final Module module = ModuleUtilCore.findModuleForFile(file, project);
        if (module == null) {
            throw new IllegalArgumentException("No module for file " + file + " in project " + project);
        }

        editor = new GuiEditor(project, module, file);

        mxEventSource.mxIEventListener mxIEventListener = (sender, evt) -> {
            //System.out.println(sender);
            System.out.println(evt.getName());
        };
        mxGraphComponent graphComponent = editor.getGraphComponent();
        mxGraph graph = graphComponent.getGraph();
//        graphComponent.addListener(mxEvent.DONE, mxIEventListener);
//        graphComponent.addListener(mxEvent.ADD_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_ADDED, mxIEventListener);
//        graphComponent.addListener(mxEvent.ALIGN_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CONNECT_CELL, mxIEventListener);
//        graphComponent.addListener(mxEvent.CONNECT, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELL_CONNECTED, mxIEventListener);
//        graphComponent.addListener(mxEvent.FLIP_EDGE, mxIEventListener);
//        graphComponent.addListener(mxEvent.FOLD_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_FOLDED, mxIEventListener);
//        graphComponent.addListener(mxEvent.GROUP_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.UNGROUP_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.REMOVE_CELLS_FROM_PARENT, mxIEventListener);
//        graphComponent.addListener(mxEvent.MOVE_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_MOVED, mxIEventListener);
//        graphComponent.addListener(mxEvent.ORDER_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_ORDERED, mxIEventListener);
//        graphComponent.addListener(mxEvent.REMOVE_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_REMOVED, mxIEventListener);
//        graphComponent.addListener(mxEvent.REPAINT, mxIEventListener);
//        graphComponent.addListener(mxEvent.RESIZE_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_RESIZED, mxIEventListener);
//        graphComponent.addListener(mxEvent.SPLIT_EDGE, mxIEventListener);
//        graphComponent.addListener(mxEvent.TOGGLE_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.CELLS_TOGGLED, mxIEventListener);
//        graphComponent.addListener(mxEvent.UPDATE_CELL_SIZE, mxIEventListener);
//        graphComponent.addListener(mxEvent.LABEL_CHANGED, mxIEventListener);
//        graphComponent.addListener(mxEvent.ADD_OVERLAY, mxIEventListener);
//        graphComponent.addListener(mxEvent.REMOVE_OVERLAY, mxIEventListener);
//        editor.getGraphComponent().addListener(mxEvent.BEFORE_PAINT, mxIEventListener);
//        editor.getGraphComponent().addListener(mxEvent.PAINT, mxIEventListener);
//        editor.getGraphComponent().addListener(mxEvent.AFTER_PAINT, mxIEventListener);
//        graphComponent.addListener(mxEvent.START_EDITING, mxIEventListener);
//        graphComponent.addListener(mxEvent.UNDO, mxIEventListener);
//        graphComponent.addListener(mxEvent.REDO, mxIEventListener);
//        graphComponent.addListener(mxEvent.UP, mxIEventListener);
//        graphComponent.addListener(mxEvent.DOWN, mxIEventListener);
//        graphComponent.addListener(mxEvent.SCALE, mxIEventListener);
//        graphComponent.addListener(mxEvent.TRANSLATE, mxIEventListener);
//        graphComponent.addListener(mxEvent.SCALE_AND_TRANSLATE, mxIEventListener);
//        graphComponent.addListener(mxEvent.CHANGE, mxIEventListener);
//        graphComponent.addListener(mxEvent.EXECUTE, mxIEventListener);
//        graphComponent.addListener(mxEvent.BEFORE_UNDO, mxIEventListener);
//        graphComponent.addListener(mxEvent.NOTIFY, mxIEventListener);
//        graphComponent.addListener(mxEvent.BEGIN_UPDATE, mxIEventListener);
//        graphComponent.addListener(mxEvent.END_UPDATE, mxIEventListener);
//        graphComponent.addListener(mxEvent.INSERT, mxIEventListener);
//        graphComponent.addListener(mxEvent.ADD, mxIEventListener);
//        graphComponent.addListener(mxEvent.CLEAR, mxIEventListener);
//        graphComponent.addListener(mxEvent.FIRED, mxIEventListener);
//        graphComponent.addListener(mxEvent.SELECT, mxIEventListener);
//        graphComponent.addListener(mxEvent.MARK, mxIEventListener);
//        graphComponent.addListener(mxEvent.ROOT, mxIEventListener);
//        graphComponent.addListener(mxEvent.LAYOUT_CELLS, mxIEventListener);
//        graphComponent.addListener(mxEvent.START, mxIEventListener);
//        graphComponent.addListener(mxEvent.CONTINUE, mxIEventListener);
//        graphComponent.addListener(mxEvent.STOP, mxIEventListener);

//        String xml = VirtualFileUtil.readText(file);
//        Document document = mxXmlUtils.parseXml(xml);
//
//        mxCodec codec = new mxCodec(document);
//        if (document != null) {
//            codec.decode(document.getDocumentElement(), editor.getGraphComponent().getGraph().getModel());
//        }
        //editor.setCurrentFile(fc.getSelectedFile());
//        editor.reset();


        //editor.createFrame(new EditorMenuBar(editor)).setVisible(true);
    }

    @Override
    public VirtualFile getFile() {
        return this.file;
    }

    /**
     * Gets editor.
     *
     * @return the editor
     */
    public GuiEditor getEditor() {
        return editor;
    }

    @Override
    public @NotNull JComponent getComponent() {
        return editor;
    }

    @Override
    public @Nullable JComponent getPreferredFocusedComponent() {
        //todo implement it
        return null;
    }

    @Override
    public @Nls(capitalization = Nls.Capitalization.Title) @NotNull String getName() {
        return "Bpm Swing Editor";
    }

    @Override
    public void setState(@NotNull FileEditorState state) {
        this.state = state;
    }

    @Override
    public boolean isModified() {
        return this.isModified;
    }

    /**
     * @return whether the editor is valid or not. An editor is valid if the contents displayed in it still exists. For example, an editor
     * displaying the contents of a file stops being valid if the file is deleted. Editor can also become invalid when it's disposed.
     */
    @Override
    public boolean isValid() {
        //TODO shunyun 2024/3/9: is valid
        return true;
    }

    @Override
    public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
        this.listeners.add(listener);
    }

    @Override
    public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
        this.listeners.remove(listener);
    }

    @Override
    public void dispose() {

    }

    @Override
    public <T> @Nullable T getUserData(@NotNull Key<T> key) {
        return (T) this.userData.get(key);
    }

    @Override
    public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
        this.userData.put(key, value);
    }

    /**
     * Go to.
     */
    public void goTo() {
        RadComponent[] selectedComponents = this.editor.getGraphComponent().getSelectedComponents();
        if (ArrayUtils.isEmpty(selectedComponents)) {
            return;
        }
        RadComponent component = selectedComponents[0];
        mxCell cell = component.getDelegee();
        if (cell.isEdge()) {
            return;
        }

        if (cell.getValue() instanceof JavaMethodTask task) {
            if (task.getCallable() != null) {
                String type = task.getCallable().getType().getQualifiedName();
                if (StringUtils.isNoneBlank(type)) {
                    FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
                    Collection<PsiClass> classes = JavaFullClassNameIndex.getInstance()
                            .getClasses(type, project, GlobalSearchScope.allScope(project));
                    if (classes.isEmpty()) {
                        return;
                    }
                    for (PsiClass aClass : classes) {
                        fileEditorManager.openFile(aClass.getContainingFile().getVirtualFile(), true);
                    }
                }
            }
        }
    }
}
